﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text.RegularExpressions;

namespace Agile.DataAccess
{
	internal class ExecuteQueryHandler
	{
		private readonly DbCommand _command;
		private bool _queryAlreadyExecuted;

		public ExecuteQueryHandler(DbCommand command)
		{
			_command = command;
		}

        private string CreateException(string sql)
        {
            MatchCollection mc = Regex.Matches(sql, "{\\w+}", RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture);
            string valueStr = string.Empty;
            String[] values = new String[mc.Count];
            for (int i = 0; i < mc.Count; i++)
                values[i] = mc[i].Value;
            foreach (string item in values.Select(x => x.Trim()).Distinct(StringComparer.InvariantCultureIgnoreCase))
                valueStr += item + ", ";
            if (!string.IsNullOrEmpty(valueStr))
                return string.Format("SQL语句中存在未赋值的变量：{0}", valueStr.Substring(0, valueStr.Length - 2));
            return string.Empty;
        }

        internal void ExecuteQuery(bool useReader, Action action)//执行查询
        {
            try
            {
                PrepareDbCommand(useReader);
                action();

                if (_command.Data.Context.Data.OnExecuted != null)
                    _command.Data.Context.Data.OnExecuted(new CommandEventArgs(_command.Data.InnerCommand));
            }
            catch (Exception exception)
            {
                string error = this.CreateException(_command.Data.Sql.ToString());
                if (string.IsNullOrEmpty(error))
                    HandleQueryException(exception);
                else
                    HandleQueryException(new Exception(error));
            }
            finally
            {
                HandleQueryFinally();
            }
        }

		private void PrepareDbCommand(bool useReader)
		{
			if (_queryAlreadyExecuted)
			{
				if(_command.Data.UseMultipleResultsets)
					_command.Data.Reader.NextResult();
				else
					throw new FluentDataException("A query has already been executed on this command object. Please create a new command object.");
			}
			else
			{
				_command.Data.InnerCommand.CommandText = _command.Data.Sql.ToString();

				if(_command.Data.Context.Data.CommandTimeout != Int32.MinValue)
					_command.Data.InnerCommand.CommandTimeout = _command.Data.Context.Data.CommandTimeout;

				if(_command.Data.InnerCommand.Connection.State != ConnectionState.Open)
					OpenConnection();

				if(_command.Data.Context.Data.UseTransaction)
				{
					if(_command.Data.Context.Data.Transaction == null)
						_command.Data.Context.Data.Transaction = _command.Data.Context.Data.Connection.BeginTransaction((System.Data.IsolationLevel)_command.Data.Context.Data.IsolationLevel);

					_command.Data.InnerCommand.Transaction = _command.Data.Context.Data.Transaction;
				}

                if (_command.Data.Context.Data.OnExecuting != null && _command.Data.Output)
                    _command.Data.Context.Data.OnExecuting(new CommandEventArgs(_command.Data.InnerCommand));

				if (useReader)
					_command.Data.Reader = new DataReader(_command.Data.InnerCommand.ExecuteReader());

				_queryAlreadyExecuted = true;
			}
		}

		private void OpenConnection()
		{
			if(_command.Data.Context.Data.OnConnectionOpening != null)
				_command.Data.Context.Data.OnConnectionOpening(new ConnectionEventArgs(_command.Data.InnerCommand.Connection));

			_command.Data.InnerCommand.Connection.Open();

			if(_command.Data.Context.Data.OnConnectionOpened != null)
				_command.Data.Context.Data.OnConnectionOpened(new ConnectionEventArgs(_command.Data.InnerCommand.Connection));
		}

		private void HandleQueryFinally()
		{
			if(!_command.Data.UseMultipleResultsets)
			{
				if(_command.Data.Reader != null)
					_command.Data.Reader.Close();

				_command.ClosePrivateConnection();
			}
		}

		private void HandleQueryException(Exception exception)
		{
			if(_command.Data.Reader != null)
				_command.Data.Reader.Close();

			_command.ClosePrivateConnection();
            if (_command.Data.Context.Data.UseTransaction)
                _command.Data.Context.CloseSharedConnection();

			if(_command.Data.Context.Data.OnError != null)
                _command.Data.Context.Data.OnError(new ErrorEventArgs(_command.Data.InnerCommand, exception));
			
			throw exception;
		}
	}
}
